home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / Direct3D / SkinnedMesh / skinnedmesh.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  77.6 KB  |  1,811 lines

  1. //--------------------------------------------------------------------------------------
  2. // File: SkinnedMesh.cpp
  3. //
  4. // Starting point for new Direct3D applications
  5. //
  6. // Copyright (c) Microsoft Corporation. All rights reserved.
  7. //--------------------------------------------------------------------------------------
  8. #include "dxstdafx.h"
  9. #include "resource.h"
  10.  
  11. //#define DEBUG_VS   // Uncomment this line to debug vertex shaders 
  12. //#define DEBUG_PS   // Uncomment this line to debug pixel shaders 
  13.  
  14. #define MESHFILENAME L"tiny\\tiny.x"
  15.  
  16.  
  17. WCHAR g_wszShaderSource[4][30] =
  18. {
  19.     L"skinmesh1.vsh",
  20.     L"skinmesh2.vsh",
  21.     L"skinmesh3.vsh",
  22.     L"skinmesh4.vsh"
  23. };
  24.  
  25.  
  26. // enum for various skinning modes possible
  27. enum METHOD
  28. {
  29.     D3DNONINDEXED,
  30.     D3DINDEXED,
  31.     SOFTWARE,
  32.     D3DINDEXEDVS,
  33.     D3DINDEXEDHLSLVS,
  34.     NONE
  35. };
  36.  
  37.  
  38. //--------------------------------------------------------------------------------------
  39. // Name: struct D3DXFRAME_DERIVED
  40. // Desc: Structure derived from D3DXFRAME so we can add some app-specific
  41. //       info that will be stored with each frame
  42. //--------------------------------------------------------------------------------------
  43. struct D3DXFRAME_DERIVED: public D3DXFRAME
  44. {
  45.     D3DXMATRIXA16        CombinedTransformationMatrix;
  46. };
  47.  
  48.  
  49. //--------------------------------------------------------------------------------------
  50. // Name: struct D3DXMESHCONTAINER_DERIVED
  51. // Desc: Structure derived from D3DXMESHCONTAINER so we can add some app-specific
  52. //       info that will be stored with each mesh
  53. //--------------------------------------------------------------------------------------
  54. struct D3DXMESHCONTAINER_DERIVED: public D3DXMESHCONTAINER
  55. {
  56.     LPDIRECT3DTEXTURE9*  ppTextures;       // array of textures, entries are NULL if no texture specified    
  57.                                 
  58.     // SkinMesh info             
  59.     LPD3DXMESH           pOrigMesh;
  60.     LPD3DXATTRIBUTERANGE pAttributeTable;
  61.     DWORD                NumAttributeGroups; 
  62.     DWORD                NumInfl;
  63.     LPD3DXBUFFER         pBoneCombinationBuf;
  64.     D3DXMATRIX**         ppBoneMatrixPtrs;
  65.     D3DXMATRIX*          pBoneOffsetMatrices;
  66.     DWORD                NumPaletteEntries;
  67.     bool                 UseSoftwareVP;
  68.     DWORD                iAttributeSW;     // used to denote the split between SW and HW if necessary for non-indexed skinning
  69. };
  70.  
  71.  
  72. //--------------------------------------------------------------------------------------
  73. // Name: class CAllocateHierarchy
  74. // Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create
  75. //       frames and meshcontainers.
  76. //--------------------------------------------------------------------------------------
  77. class CAllocateHierarchy: public ID3DXAllocateHierarchy
  78. {
  79. public:
  80.     STDMETHOD(CreateFrame)(THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame);
  81.     STDMETHOD(CreateMeshContainer)(THIS_ 
  82.         LPCSTR Name, 
  83.         CONST D3DXMESHDATA *pMeshData,
  84.         CONST D3DXMATERIAL *pMaterials, 
  85.         CONST D3DXEFFECTINSTANCE *pEffectInstances, 
  86.         DWORD NumMaterials, 
  87.         CONST DWORD *pAdjacency, 
  88.         LPD3DXSKININFO pSkinInfo, 
  89.         LPD3DXMESHCONTAINER *ppNewMeshContainer);
  90.     STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree);
  91.     STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase);
  92.  
  93.     CAllocateHierarchy() {}
  94. };
  95.  
  96.  
  97. //--------------------------------------------------------------------------------------
  98. // Global variables
  99. //--------------------------------------------------------------------------------------
  100. ID3DXFont*              g_pFont = NULL;         // Font for drawing text
  101. ID3DXSprite*            g_pTextSprite = NULL;   // Sprite for batching draw text calls
  102. ID3DXEffect*            g_pEffect = NULL;       // D3DX effect interface
  103. CD3DArcBall             g_ArcBall;              // Arcball for model control
  104. bool                    g_bShowHelp = true;     // If true, it renders the UI control text
  105. CDXUTDialog             g_HUD;                  // dialog for standard controls
  106. CDXUTDialog             g_SampleUI;             // dialog for sample specific controls
  107. LPD3DXFRAME             g_pFrameRoot = NULL;
  108. ID3DXAnimationController* g_pAnimController = NULL;
  109. D3DXVECTOR3             g_vObjectCenter;        // Center of bounding sphere of object
  110. FLOAT                   g_fObjectRadius;        // Radius of bounding sphere of object
  111. METHOD                  g_SkinningMethod = D3DNONINDEXED; // Current skinning method
  112. D3DXMATRIXA16*          g_pBoneMatrices = NULL;
  113. UINT                    g_NumBoneMatricesMax = 0;
  114. IDirect3DVertexShader9* g_pIndexedVertexShader[4];
  115. D3DXMATRIXA16           g_matView;              // View matrix
  116. D3DXMATRIXA16           g_matProj;              // Projection matrix
  117. D3DXMATRIXA16           g_matProjT;             // Transpose of projection matrix (for asm shader)
  118. DWORD                   g_dwBehaviorFlags;      // Behavior flags of the 3D device
  119. bool                    g_bUseSoftwareVP;       // Flag to indicate whether software vp is
  120.                                                 // required due to lack of hardware
  121.  
  122.  
  123. //--------------------------------------------------------------------------------------
  124. // UI control IDs
  125. //--------------------------------------------------------------------------------------
  126. #define IDC_TOGGLEFULLSCREEN    1
  127. #define IDC_TOGGLEREF           3
  128. #define IDC_CHANGEDEVICE        4
  129. #define IDC_METHOD              5
  130.  
  131.  
  132.  
  133. //--------------------------------------------------------------------------------------
  134. // Forward declarations 
  135. //--------------------------------------------------------------------------------------
  136. bool    CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed );
  137. void    CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps );
  138. HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc );
  139. HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc );
  140. void    CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime );
  141. void    CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime );
  142. LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing );
  143. void    CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown  );
  144. void    CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl );
  145. void    CALLBACK OnLostDevice();
  146. void    CALLBACK OnDestroyDevice();
  147.  
  148. void    InitApp();
  149. HRESULT LoadMesh( IDirect3DDevice9* pd3dDevice, WCHAR* strFileName, ID3DXMesh** ppMesh );
  150. void    RenderText();
  151. void    DrawMeshContainer( IDirect3DDevice9 *pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase );
  152. void    DrawFrame( IDirect3DDevice9 *pd3dDevice, LPD3DXFRAME pFrame );
  153. HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainer );
  154. HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrame );
  155. void    UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix );
  156. void    UpdateSkinningMethod( LPD3DXFRAME pFrameBase );
  157. HRESULT GenerateSkinnedMesh( IDirect3DDevice9 *pd3dDevice, D3DXMESHCONTAINER_DERIVED *pMeshContainer );
  158.  
  159.  
  160. //--------------------------------------------------------------------------------------
  161. // Name: AllocateName()
  162. // Desc: Allocates memory for a string to hold the name of a frame or mesh
  163. //--------------------------------------------------------------------------------------
  164. HRESULT AllocateName( LPCSTR Name, LPSTR *pNewName )
  165. {
  166.     UINT cbLength;
  167.  
  168.     if( Name != NULL )
  169.     {
  170.         cbLength = (UINT)strlen(Name) + 1;
  171.         *pNewName = new CHAR[cbLength];
  172.         if (*pNewName == NULL)
  173.             return E_OUTOFMEMORY;
  174.         memcpy( *pNewName, Name, cbLength*sizeof(CHAR) );
  175.     }
  176.     else
  177.     {
  178.         *pNewName = NULL;
  179.     }
  180.  
  181.     return S_OK;
  182. }
  183.  
  184.  
  185.  
  186.  
  187. //--------------------------------------------------------------------------------------
  188. // Name: CAllocateHierarchy::CreateFrame()
  189. // Desc: 
  190. //--------------------------------------------------------------------------------------
  191. HRESULT CAllocateHierarchy::CreateFrame( LPCSTR Name, LPD3DXFRAME *ppNewFrame )
  192. {
  193.     HRESULT hr = S_OK;
  194.     D3DXFRAME_DERIVED *pFrame;
  195.  
  196.     *ppNewFrame = NULL;
  197.  
  198.     pFrame = new D3DXFRAME_DERIVED;
  199.     if (pFrame == NULL)
  200.     {
  201.         hr = E_OUTOFMEMORY;
  202.         goto e_Exit;
  203.     }
  204.  
  205.     hr = AllocateName(Name, &pFrame->Name);
  206.     if (FAILED(hr))
  207.         goto e_Exit;
  208.  
  209.     // initialize other data members of the frame
  210.     D3DXMatrixIdentity(&pFrame->TransformationMatrix);
  211.     D3DXMatrixIdentity(&pFrame->CombinedTransformationMatrix);
  212.  
  213.     pFrame->pMeshContainer = NULL;
  214.     pFrame->pFrameSibling = NULL;
  215.     pFrame->pFrameFirstChild = NULL;
  216.  
  217.     *ppNewFrame = pFrame;
  218.     pFrame = NULL;
  219.  
  220. e_Exit:
  221.     delete pFrame;
  222.     return hr;
  223. }
  224.  
  225.  
  226.  
  227.  
  228. //--------------------------------------------------------------------------------------
  229. // Name: CAllocateHierarchy::CreateMeshContainer()
  230. // Desc: 
  231. //--------------------------------------------------------------------------------------
  232. HRESULT CAllocateHierarchy::CreateMeshContainer(
  233.     LPCSTR Name, 
  234.     CONST D3DXMESHDATA *pMeshData,
  235.     CONST D3DXMATERIAL *pMaterials, 
  236.     CONST D3DXEFFECTINSTANCE *pEffectInstances, 
  237.     DWORD NumMaterials, 
  238.     CONST DWORD *pAdjacency, 
  239.     LPD3DXSKININFO pSkinInfo, 
  240.     LPD3DXMESHCONTAINER *ppNewMeshContainer) 
  241. {
  242.     HRESULT hr;
  243.     D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL;
  244.     UINT NumFaces;
  245.     UINT iMaterial;
  246.     UINT iBone, cBones;
  247.     LPDIRECT3DDEVICE9 pd3dDevice = NULL;
  248.  
  249.     LPD3DXMESH pMesh = NULL;
  250.  
  251.     *ppNewMeshContainer = NULL;
  252.  
  253.     // this sample does not handle patch meshes, so fail when one is found
  254.     if (pMeshData->Type != D3DXMESHTYPE_MESH)
  255.     {
  256.         hr = E_FAIL;
  257.         goto e_Exit;
  258.     }
  259.  
  260.     // get the pMesh interface pointer out of the mesh data structure
  261.     pMesh = pMeshData->pMesh;
  262.  
  263.     // this sample does not FVF compatible meshes, so fail when one is found
  264.     if (pMesh->GetFVF() == 0)
  265.     {
  266.         hr = E_FAIL;
  267.         goto e_Exit;
  268.     }
  269.  
  270.     // allocate the overloaded structure to return as a D3DXMESHCONTAINER
  271.     pMeshContainer = new D3DXMESHCONTAINER_DERIVED;
  272.     if (pMeshContainer == NULL)
  273.     {
  274.         hr = E_OUTOFMEMORY;
  275.         goto e_Exit;
  276.     }
  277.     memset(pMeshContainer, 0, sizeof(D3DXMESHCONTAINER_DERIVED));
  278.  
  279.     // make sure and copy the name.  All memory as input belongs to caller, interfaces can be addref'd though
  280.     hr = AllocateName(Name, &pMeshContainer->Name);
  281.     if (FAILED(hr))
  282.         goto e_Exit;        
  283.  
  284.     pMesh->GetDevice(&pd3dDevice);
  285.     NumFaces = pMesh->GetNumFaces();
  286.  
  287.     // if no normals are in the mesh, add them
  288.     if (!(pMesh->GetFVF() & D3DFVF_NORMAL))
  289.     {
  290.         pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
  291.  
  292.         // clone the mesh to make room for the normals
  293.         hr = pMesh->CloneMeshFVF( pMesh->GetOptions(), 
  294.                                     pMesh->GetFVF() | D3DFVF_NORMAL, 
  295.                                     pd3dDevice, &pMeshContainer->MeshData.pMesh );
  296.         if (FAILED(hr))
  297.             goto e_Exit;
  298.  
  299.         // get the new pMesh pointer back out of the mesh container to use
  300.         // NOTE: we do not release pMesh because we do not have a reference to it yet
  301.         pMesh = pMeshContainer->MeshData.pMesh;
  302.  
  303.         // now generate the normals for the pmesh
  304.         D3DXComputeNormals( pMesh, NULL );
  305.     }
  306.     else  // if no normals, just add a reference to the mesh for the mesh container
  307.     {
  308.         pMeshContainer->MeshData.pMesh = pMesh;
  309.         pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
  310.  
  311.         pMesh->AddRef();
  312.     }
  313.         
  314.     // allocate memory to contain the material information.  This sample uses
  315.     //   the D3D9 materials and texture names instead of the EffectInstance style materials
  316.     pMeshContainer->NumMaterials = max(1, NumMaterials);
  317.     pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
  318.     pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];
  319.     pMeshContainer->pAdjacency = new DWORD[NumFaces*3];
  320.     if ((pMeshContainer->pAdjacency == NULL) || (pMeshContainer->pMaterials == NULL))
  321.     {
  322.         hr = E_OUTOFMEMORY;
  323.         goto e_Exit;
  324.     }
  325.  
  326.     memcpy(pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * NumFaces*3);
  327.     memset(pMeshContainer->ppTextures, 0, sizeof(LPDIRECT3DTEXTURE9) * pMeshContainer->NumMaterials);
  328.  
  329.     // if materials provided, copy them
  330.     if (NumMaterials > 0)            
  331.     {
  332.         memcpy(pMeshContainer->pMaterials, pMaterials, sizeof(D3DXMATERIAL) * NumMaterials);
  333.  
  334.         for (iMaterial = 0; iMaterial < NumMaterials; iMaterial++)
  335.         {
  336.             if (pMeshContainer->pMaterials[iMaterial].pTextureFilename != NULL)
  337.             {
  338.                 WCHAR strTexturePath[MAX_PATH];
  339.                 WCHAR wszBuf[MAX_PATH];
  340.                 MultiByteToWideChar( CP_ACP, 0, pMeshContainer->pMaterials[iMaterial].pTextureFilename, -1, wszBuf, MAX_PATH );
  341.                 wszBuf[MAX_PATH - 1] = L'\0';
  342.                 DXUTFindDXSDKMediaFileCch( strTexturePath, MAX_PATH, wszBuf );
  343.                 if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, strTexturePath,
  344.                                                         &pMeshContainer->ppTextures[iMaterial] ) ) )
  345.                     pMeshContainer->ppTextures[iMaterial] = NULL;
  346.  
  347.                 // don't remember a pointer into the dynamic memory, just forget the name after loading
  348.                 pMeshContainer->pMaterials[iMaterial].pTextureFilename = NULL;
  349.             }
  350.         }
  351.     }
  352.     else // if no materials provided, use a default one
  353.     {
  354.         pMeshContainer->pMaterials[0].pTextureFilename = NULL;
  355.         memset(&pMeshContainer->pMaterials[0].MatD3D, 0, sizeof(D3DMATERIAL9));
  356.         pMeshContainer->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
  357.         pMeshContainer->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
  358.         pMeshContainer->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
  359.         pMeshContainer->pMaterials[0].MatD3D.Specular = pMeshContainer->pMaterials[0].MatD3D.Diffuse;
  360.     }
  361.  
  362.     // if there is skinning information, save off the required data and then setup for HW skinning
  363.     if (pSkinInfo != NULL)
  364.     {
  365.         // first save off the SkinInfo and original mesh data
  366.         pMeshContainer->pSkinInfo = pSkinInfo;
  367.         pSkinInfo->AddRef();
  368.  
  369.         pMeshContainer->pOrigMesh = pMesh;
  370.         pMesh->AddRef();
  371.  
  372.         // Will need an array of offset matrices to move the vertices from the figure space to the bone's space
  373.         cBones = pSkinInfo->GetNumBones();
  374.         pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX[cBones];
  375.         if (pMeshContainer->pBoneOffsetMatrices == NULL)
  376.         {
  377.             hr = E_OUTOFMEMORY;
  378.             goto e_Exit;
  379.         }
  380.  
  381.         // get each of the bone offset matrices so that we don't need to get them later
  382.         for (iBone = 0; iBone < cBones; iBone++)
  383.         {
  384.             pMeshContainer->pBoneOffsetMatrices[iBone] = *(pMeshContainer->pSkinInfo->GetBoneOffsetMatrix(iBone));
  385.         }
  386.  
  387.         // GenerateSkinnedMesh will take the general skinning information and transform it to a HW friendly version
  388.         hr = GenerateSkinnedMesh( pd3dDevice, pMeshContainer );
  389.         if (FAILED(hr))
  390.             goto e_Exit;
  391.     }
  392.  
  393.     *ppNewMeshContainer = pMeshContainer;
  394.     pMeshContainer = NULL;
  395.  
  396. e_Exit:
  397.     SAFE_RELEASE(pd3dDevice);
  398.  
  399.     // call Destroy function to properly clean up the memory allocated 
  400.     if (pMeshContainer != NULL)
  401.     {
  402.         DestroyMeshContainer(pMeshContainer);
  403.     }
  404.  
  405.     return hr;
  406. }
  407.  
  408.  
  409.  
  410.  
  411. //--------------------------------------------------------------------------------------
  412. // Name: CAllocateHierarchy::DestroyFrame()
  413. // Desc: 
  414. //--------------------------------------------------------------------------------------
  415. HRESULT CAllocateHierarchy::DestroyFrame(LPD3DXFRAME pFrameToFree) 
  416. {
  417.     SAFE_DELETE_ARRAY( pFrameToFree->Name );
  418.     SAFE_DELETE( pFrameToFree );
  419.     return S_OK; 
  420. }
  421.  
  422.  
  423.  
  424.  
  425. //--------------------------------------------------------------------------------------
  426. // Name: CAllocateHierarchy::DestroyMeshContainer()
  427. // Desc: 
  428. //--------------------------------------------------------------------------------------
  429. HRESULT CAllocateHierarchy::DestroyMeshContainer(LPD3DXMESHCONTAINER pMeshContainerBase)
  430. {
  431.     UINT iMaterial;
  432.     D3DXMESHCONTAINER_DERIVED *pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;
  433.  
  434.     SAFE_DELETE_ARRAY( pMeshContainer->Name );
  435.     SAFE_DELETE_ARRAY( pMeshContainer->pAdjacency );
  436.     SAFE_DELETE_ARRAY( pMeshContainer->pMaterials );
  437.     SAFE_DELETE_ARRAY( pMeshContainer->pBoneOffsetMatrices );
  438.  
  439.     // release all the allocated textures
  440.     if (pMeshContainer->ppTextures != NULL)
  441.     {
  442.         for (iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++)
  443.         {
  444.             SAFE_RELEASE( pMeshContainer->ppTextures[iMaterial] );
  445.         }
  446.     }
  447.  
  448.     SAFE_DELETE_ARRAY( pMeshContainer->ppTextures );
  449.     SAFE_DELETE_ARRAY( pMeshContainer->ppBoneMatrixPtrs );
  450.     SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
  451.     SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
  452.     SAFE_RELEASE( pMeshContainer->pSkinInfo );
  453.     SAFE_RELEASE( pMeshContainer->pOrigMesh );
  454.     SAFE_DELETE( pMeshContainer );
  455.     return S_OK;
  456. }
  457.  
  458.  
  459. //--------------------------------------------------------------------------------------
  460. // Entry point to the program. Initializes everything and goes into a message processing 
  461. // loop. Idle time is used to render the scene.
  462. //--------------------------------------------------------------------------------------
  463. INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
  464. {
  465.     // Set the callback functions. These functions allow the sample framework to notify
  466.     // the application about device changes, user input, and windows messages.  The 
  467.     // callbacks are optional so you need only set callbacks for events you're interested 
  468.     // in. However, if you don't handle the device reset/lost callbacks then the sample 
  469.     // framework won't be able to reset your device since the application must first 
  470.     // release all device resources before resetting.  Likewise, if you don't handle the 
  471.     // device created/destroyed callbacks then the sample framework won't be able to 
  472.     // recreate your device resources.
  473.     DXUTSetCallbackDeviceCreated( OnCreateDevice );
  474.     DXUTSetCallbackDeviceReset( OnResetDevice );
  475.     DXUTSetCallbackDeviceLost( OnLostDevice );
  476.     DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
  477.     DXUTSetCallbackMsgProc( MsgProc );
  478.     DXUTSetCallbackKeyboard( KeyboardProc );
  479.     DXUTSetCallbackFrameRender( OnFrameRender );
  480.     DXUTSetCallbackFrameMove( OnFrameMove );
  481.  
  482.     // Show the cursor and clip it when in full screen
  483.     DXUTSetCursorSettings( true, true );
  484.  
  485.     InitApp();
  486.  
  487.     // Initialize the sample framework and create the desired Win32 window and Direct3D 
  488.     // device for the application. Calling each of these functions is optional, but they
  489.     // allow you to set several options which control the behavior of the framework.
  490.     DXUTInit( true, true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes
  491.     DXUTCreateWindow( L"Skinned Mesh" );
  492.  
  493.     // Supports all types of vertex processing, including mixed.
  494.     DXUTGetEnumeration()->SetPossibleVertexProcessingList( true, true, true, true );
  495.  
  496.     DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 640, 480, IsDeviceAcceptable, ModifyDeviceSettings );
  497.  
  498.     // Pass control to the sample framework for handling the message pump and 
  499.     // dispatching render calls. The sample framework will call your FrameMove 
  500.     // and FrameRender callback when there is idle time between handling window messages.
  501.     DXUTMainLoop();
  502.  
  503.     // Perform any application-level cleanup here. Direct3D device resources are released within the
  504.     // appropriate callback functions and therefore don't require any cleanup code here.
  505.  
  506.     return DXUTGetExitCode();
  507. }
  508.  
  509.  
  510. //--------------------------------------------------------------------------------------
  511. // Initialize the app 
  512. //--------------------------------------------------------------------------------------
  513. void InitApp()
  514. {
  515.     // Initialize dialogs
  516.     g_HUD.SetCallback( OnGUIEvent ); int iY = 10; 
  517.     g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen", 35, iY, 125, 22 );
  518.     g_HUD.AddButton( IDC_TOGGLEREF, L"Toggle REF (F3)", 35, iY += 24, 125, 22 );
  519.     g_HUD.AddButton( IDC_CHANGEDEVICE, L"Change device (F2)", 35, iY += 24, 125, 22 );
  520.  
  521.     g_SampleUI.SetCallback( OnGUIEvent ); iY = 10;
  522.     g_SampleUI.AddComboBox( IDC_METHOD, 0, iY, 230, 24, L'S' );
  523.     g_SampleUI.GetComboBox( IDC_METHOD )->AddItem( L"Fixed function non-indexed (s)kinning", (void*)D3DNONINDEXED );
  524.     g_SampleUI.GetComboBox( IDC_METHOD )->AddItem( L"Fixed function indexed (s)kinning", (void*)D3DINDEXED );
  525.     g_SampleUI.GetComboBox( IDC_METHOD )->AddItem( L"Software (s)kinning", (void*)SOFTWARE );
  526.     g_SampleUI.GetComboBox( IDC_METHOD )->AddItem( L"ASM shader indexed (s)kinning", (void*)D3DINDEXEDVS );
  527.     g_SampleUI.GetComboBox( IDC_METHOD )->AddItem( L"HLSL shader indexed (s)kinning", (void*)D3DINDEXEDHLSLVS );
  528. }
  529.  
  530.  
  531. //--------------------------------------------------------------------------------------
  532. // Called during device initialization, this code checks the device for some 
  533. // minimum set of capabilities, and rejects those that don't pass by returning false.
  534. //--------------------------------------------------------------------------------------
  535. bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, 
  536.                                   D3DFORMAT BackBufferFormat, bool bWindowed )
  537. {
  538.     // Skip backbuffer formats that don't support alpha blending
  539.     IDirect3D9* pD3D = DXUTGetD3DObject(); 
  540.     if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
  541.                     AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, 
  542.                     D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
  543.         return false;
  544.  
  545.     return true;
  546. }
  547.  
  548.  
  549. //--------------------------------------------------------------------------------------
  550. // This callback function is called immediately before a device is created to allow the 
  551. // application to modify the device settings. The supplied pDeviceSettings parameter 
  552. // contains the settings that the framework has selected for the new device, and the 
  553. // application can make any desired changes directly to this structure.  Note however that 
  554. // the sample framework will not correct invalid device settings so care must be taken 
  555. // to return valid device settings, otherwise IDirect3D9::CreateDevice() will fail.  
  556. //--------------------------------------------------------------------------------------
  557. void CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps )
  558. {
  559.     // If device doesn't support HW T&L or doesn't support 1.1 vertex shaders in HW 
  560.     // then switch to SWVP.
  561.     if( (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 ||
  562.          pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) )
  563.     {
  564.         pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
  565.     }
  566.     else
  567.     {
  568.         pDeviceSettings->BehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
  569.     }
  570.  
  571.     // This application is designed to work on a pure device by not using 
  572.     // IDirect3D9::Get*() methods, so create a pure device if supported and using HWVP.
  573.     if ((pCaps->DevCaps & D3DDEVCAPS_PUREDEVICE) != 0 && 
  574.         (pDeviceSettings->BehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING) != 0 )
  575.         pDeviceSettings->BehaviorFlags |= D3DCREATE_PUREDEVICE;
  576.  
  577.     // If the hardware cannot do vertex blending, use software vertex processing.
  578.     if( pCaps->MaxVertexBlendMatrices < 2 )
  579.         pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
  580.  
  581.     // If using hardware vertex processing, change to mixed vertex processing
  582.     // so there is a fallback.
  583.     if( pDeviceSettings->BehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING )
  584.         pDeviceSettings->BehaviorFlags = D3DCREATE_MIXED_VERTEXPROCESSING;    
  585.  
  586.     // Debugging vertex shaders requires either REF or software vertex processing 
  587.     // and debugging pixel shaders requires REF.  
  588. #ifdef DEBUG_VS
  589.     if( pDeviceSettings->DeviceType != D3DDEVTYPE_REF )
  590.     {
  591.         pDeviceSettings->BehaviorFlags &= ~D3DCREATE_HARDWARE_VERTEXPROCESSING;
  592.         pDeviceSettings->BehaviorFlags &= ~D3DCREATE_PUREDEVICE;
  593.         pDeviceSettings->BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
  594.     }
  595. #endif
  596. #ifdef DEBUG_PS
  597.     pDeviceSettings->DeviceType = D3DDEVTYPE_REF;
  598. #endif
  599. }
  600.  
  601.  
  602. //--------------------------------------------------------------------------------------
  603. // Called either by CreateMeshContainer when loading a skin mesh, or when 
  604. // changing methods.  This function uses the pSkinInfo of the mesh 
  605. // container to generate the desired drawable mesh and bone combination 
  606. // table.
  607. //--------------------------------------------------------------------------------------
  608. HRESULT GenerateSkinnedMesh( IDirect3DDevice9 *pd3dDevice, D3DXMESHCONTAINER_DERIVED *pMeshContainer )
  609. {
  610.     HRESULT hr = S_OK;
  611.     D3DCAPS9 d3dCaps;
  612.     pd3dDevice->GetDeviceCaps( &d3dCaps );
  613.  
  614.     if( pMeshContainer->pSkinInfo == NULL )
  615.         return hr;
  616.  
  617.     g_bUseSoftwareVP = false;
  618.  
  619.     SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
  620.     SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
  621.  
  622.     // if non-indexed skinning mode selected, use ConvertToBlendedMesh to generate drawable mesh
  623.     if( g_SkinningMethod == D3DNONINDEXED )
  624.     {
  625.  
  626.         hr = pMeshContainer->pSkinInfo->ConvertToBlendedMesh
  627.                                    (
  628.                                        pMeshContainer->pOrigMesh,
  629.                                        D3DXMESH_MANAGED|D3DXMESHOPT_VERTEXCACHE, 
  630.                                        pMeshContainer->pAdjacency, 
  631.                                        NULL, NULL, NULL, 
  632.                                        &pMeshContainer->NumInfl,
  633.                                        &pMeshContainer->NumAttributeGroups, 
  634.                                        &pMeshContainer->pBoneCombinationBuf, 
  635.                                        &pMeshContainer->MeshData.pMesh
  636.                                    );
  637.         if (FAILED(hr))
  638.             goto e_Exit;
  639.  
  640.  
  641.         // If the device can only do 2 matrix blends, ConvertToBlendedMesh cannot approximate all meshes to it
  642.         // Thus we split the mesh in two parts: The part that uses at most 2 matrices and the rest. The first is
  643.         // drawn using the device's HW vertex processing and the rest is drawn using SW vertex processing.
  644.         LPD3DXBONECOMBINATION rgBoneCombinations  = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer());
  645.  
  646.         // look for any set of bone combinations that do not fit the caps
  647.         for (pMeshContainer->iAttributeSW = 0; pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups; pMeshContainer->iAttributeSW++)
  648.         {
  649.             DWORD cInfl   = 0;
  650.  
  651.             for (DWORD iInfl = 0; iInfl < pMeshContainer->NumInfl; iInfl++)
  652.             {
  653.                 if (rgBoneCombinations[pMeshContainer->iAttributeSW].BoneId[iInfl] != UINT_MAX)
  654.                 {
  655.                     ++cInfl;
  656.                 }
  657.             }
  658.  
  659.             if (cInfl > d3dCaps.MaxVertexBlendMatrices)
  660.             {
  661.                 break;
  662.             }
  663.         }
  664.  
  665.         // if there is both HW and SW, add the Software Processing flag
  666.         if (pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups)
  667.         {
  668.             LPD3DXMESH pMeshTmp;
  669.  
  670.             hr = pMeshContainer->MeshData.pMesh->CloneMeshFVF(D3DXMESH_SOFTWAREPROCESSING|pMeshContainer->MeshData.pMesh->GetOptions(), 
  671.                                                 pMeshContainer->MeshData.pMesh->GetFVF(),
  672.                                                 pd3dDevice, &pMeshTmp);
  673.             if (FAILED(hr))
  674.             {
  675.                 goto e_Exit;
  676.             }
  677.  
  678.             pMeshContainer->MeshData.pMesh->Release();
  679.             pMeshContainer->MeshData.pMesh = pMeshTmp;
  680.             pMeshTmp = NULL;
  681.         }
  682.     }
  683.     // if indexed skinning mode selected, use ConvertToIndexedsBlendedMesh to generate drawable mesh
  684.     else if( g_SkinningMethod == D3DINDEXED )
  685.     {
  686.         DWORD NumMaxFaceInfl;
  687.         DWORD Flags = D3DXMESHOPT_VERTEXCACHE;
  688.  
  689.         LPDIRECT3DINDEXBUFFER9 pIB;
  690.         hr = pMeshContainer->pOrigMesh->GetIndexBuffer(&pIB);
  691.         if (FAILED(hr))
  692.             goto e_Exit;
  693.  
  694.         hr = pMeshContainer->pSkinInfo->GetMaxFaceInfluences(pIB, pMeshContainer->pOrigMesh->GetNumFaces(), &NumMaxFaceInfl);
  695.         pIB->Release();
  696.         if (FAILED(hr))
  697.             goto e_Exit;
  698.  
  699.         // 12 entry palette guarantees that any triangle (4 independent influences per vertex of a tri)
  700.         // can be handled
  701.         NumMaxFaceInfl = min(NumMaxFaceInfl, 12);
  702.  
  703.         if( d3dCaps.MaxVertexBlendMatrixIndex + 1 < NumMaxFaceInfl )
  704.         {
  705.             // HW does not support indexed vertex blending. Use SW instead
  706.             pMeshContainer->NumPaletteEntries = min(256, pMeshContainer->pSkinInfo->GetNumBones());
  707.             pMeshContainer->UseSoftwareVP = true;
  708.             g_bUseSoftwareVP = true;
  709.             Flags |= D3DXMESH_SYSTEMMEM;
  710.         }
  711.         else
  712.         {
  713.             // using hardware - determine palette size from caps and number of bones
  714.             // If normals are present in the vertex data that needs to be blended for lighting, then 
  715.             // the number of matrices is half the number specified by MaxVertexBlendMatrixIndex.
  716.             pMeshContainer->NumPaletteEntries = min( ( d3dCaps.MaxVertexBlendMatrixIndex + 1 ) / 2, 
  717.                                                      pMeshContainer->pSkinInfo->GetNumBones() );
  718.             pMeshContainer->UseSoftwareVP = false;
  719.             Flags |= D3DXMESH_MANAGED;
  720.         }
  721.  
  722.         hr = pMeshContainer->pSkinInfo->ConvertToIndexedBlendedMesh
  723.                                                 (
  724.                                                 pMeshContainer->pOrigMesh,
  725.                                                 Flags, 
  726.                                                 pMeshContainer->NumPaletteEntries, 
  727.                                                 pMeshContainer->pAdjacency, 
  728.                                                 NULL, NULL, NULL, 
  729.                                                 &pMeshContainer->NumInfl,
  730.                                                 &pMeshContainer->NumAttributeGroups, 
  731.                                                 &pMeshContainer->pBoneCombinationBuf, 
  732.                                                 &pMeshContainer->MeshData.pMesh);
  733.         if (FAILED(hr))
  734.             goto e_Exit;
  735.     }
  736.     // if vertex shader indexed skinning mode selected, use ConvertToIndexedsBlendedMesh to generate drawable mesh
  737.     else if( ( g_SkinningMethod == D3DINDEXEDVS ) || ( g_SkinningMethod == D3DINDEXEDHLSLVS ) )
  738.     {
  739.         // Get palette size
  740.         // First 9 constants are used for other data.  Each 4x3 matrix takes up 3 constants.
  741.         // (96 - 9) /3 i.e. Maximum constant count - used constants 
  742.         UINT MaxMatrices = 26; 
  743.         pMeshContainer->NumPaletteEntries = min(MaxMatrices, pMeshContainer->pSkinInfo->GetNumBones());
  744.  
  745.         DWORD Flags = D3DXMESHOPT_VERTEXCACHE;
  746.         if (d3dCaps.VertexShaderVersion >= D3DVS_VERSION(1, 1))
  747.         {
  748.             pMeshContainer->UseSoftwareVP = false;
  749.             Flags |= D3DXMESH_MANAGED;
  750.         }
  751.         else
  752.         {
  753.             pMeshContainer->UseSoftwareVP = true;
  754.             g_bUseSoftwareVP = true;
  755.             Flags |= D3DXMESH_SYSTEMMEM;
  756.         }
  757.  
  758.         SAFE_RELEASE(pMeshContainer->MeshData.pMesh);
  759.  
  760.         hr = pMeshContainer->pSkinInfo->ConvertToIndexedBlendedMesh
  761.                                                 (
  762.                                                 pMeshContainer->pOrigMesh,
  763.                                                 Flags, 
  764.                                                 pMeshContainer->NumPaletteEntries, 
  765.                                                 pMeshContainer->pAdjacency, 
  766.                                                 NULL, NULL, NULL,             
  767.                                                 &pMeshContainer->NumInfl,
  768.                                                 &pMeshContainer->NumAttributeGroups, 
  769.                                                 &pMeshContainer->pBoneCombinationBuf, 
  770.                                                 &pMeshContainer->MeshData.pMesh);
  771.         if (FAILED(hr))
  772.             goto e_Exit;
  773.  
  774.  
  775.         // FVF has to match our declarator. Vertex shaders are not as forgiving as FF pipeline
  776.         DWORD NewFVF = (pMeshContainer->MeshData.pMesh->GetFVF() & D3DFVF_POSITION_MASK) | D3DFVF_NORMAL | D3DFVF_TEX1 | D3DFVF_LASTBETA_UBYTE4;
  777.         if (NewFVF != pMeshContainer->MeshData.pMesh->GetFVF())
  778.         {
  779.             LPD3DXMESH pMesh;
  780.             hr = pMeshContainer->MeshData.pMesh->CloneMeshFVF(pMeshContainer->MeshData.pMesh->GetOptions(), NewFVF, pd3dDevice, &pMesh);
  781.             if (!FAILED(hr))
  782.             {
  783.                 pMeshContainer->MeshData.pMesh->Release();
  784.                 pMeshContainer->MeshData.pMesh = pMesh;
  785.                 pMesh = NULL;
  786.             }
  787.         }
  788.  
  789.         D3DVERTEXELEMENT9 pDecl[MAX_FVF_DECL_SIZE];
  790.         LPD3DVERTEXELEMENT9 pDeclCur;
  791.         hr = pMeshContainer->MeshData.pMesh->GetDeclaration(pDecl);
  792.         if (FAILED(hr))
  793.             goto e_Exit;
  794.  
  795.         // the vertex shader is expecting to interpret the UBYTE4 as a D3DCOLOR, so update the type 
  796.         //   NOTE: this cannot be done with CloneMesh, that would convert the UBYTE4 data to float and then to D3DCOLOR
  797.         //          this is more of a "cast" operation
  798.         pDeclCur = pDecl;
  799.         while (pDeclCur->Stream != 0xff)
  800.         {
  801.             if ((pDeclCur->Usage == D3DDECLUSAGE_BLENDINDICES) && (pDeclCur->UsageIndex == 0))
  802.                 pDeclCur->Type = D3DDECLTYPE_D3DCOLOR;
  803.             pDeclCur++;
  804.         }
  805.  
  806.         hr = pMeshContainer->MeshData.pMesh->UpdateSemantics(pDecl);
  807.         if (FAILED(hr))
  808.             goto e_Exit;
  809.  
  810.         // allocate a buffer for bone matrices, but only if another mesh has not allocated one of the same size or larger
  811.         if( g_NumBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones() )
  812.         {
  813.             g_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones();
  814.  
  815.             // Allocate space for blend matrices
  816.             delete[] g_pBoneMatrices; 
  817.             g_pBoneMatrices  = new D3DXMATRIXA16[g_NumBoneMatricesMax];
  818.             if( g_pBoneMatrices == NULL )
  819.             {
  820.                 hr = E_OUTOFMEMORY;
  821.                 goto e_Exit;
  822.             }
  823.         }
  824.  
  825.     }
  826.     // if software skinning selected, use GenerateSkinnedMesh to create a mesh that can be used with UpdateSkinnedMesh
  827.     else if( g_SkinningMethod == SOFTWARE )
  828.     {
  829.         hr = pMeshContainer->pOrigMesh->CloneMeshFVF(D3DXMESH_MANAGED, pMeshContainer->pOrigMesh->GetFVF(),
  830.                                               pd3dDevice, &pMeshContainer->MeshData.pMesh);
  831.         if (FAILED(hr))
  832.             goto e_Exit;
  833.  
  834.         hr = pMeshContainer->MeshData.pMesh->GetAttributeTable(NULL, &pMeshContainer->NumAttributeGroups);
  835.         if (FAILED(hr))
  836.             goto e_Exit;
  837.  
  838.         delete[] pMeshContainer->pAttributeTable;
  839.         pMeshContainer->pAttributeTable  = new D3DXATTRIBUTERANGE[pMeshContainer->NumAttributeGroups];
  840.         if (pMeshContainer->pAttributeTable == NULL)
  841.         {
  842.             hr = E_OUTOFMEMORY;
  843.             goto e_Exit;
  844.         }
  845.  
  846.         hr = pMeshContainer->MeshData.pMesh->GetAttributeTable(pMeshContainer->pAttributeTable, NULL);
  847.         if (FAILED(hr))
  848.             goto e_Exit;
  849.  
  850.         // allocate a buffer for bone matrices, but only if another mesh has not allocated one of the same size or larger
  851.         if (g_NumBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones())
  852.         {
  853.             g_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones();
  854.  
  855.             // Allocate space for blend matrices
  856.             delete[] g_pBoneMatrices; 
  857.             g_pBoneMatrices  = new D3DXMATRIXA16[g_NumBoneMatricesMax];
  858.             if( g_pBoneMatrices == NULL )
  859.             {
  860.                 hr = E_OUTOFMEMORY;
  861.                 goto e_Exit;
  862.             }
  863.         }
  864.     }
  865.     else  // invalid g_SkinningMethod value
  866.     {        
  867.         // return failure due to invalid skinning method value
  868.         hr = E_INVALIDARG;
  869.         goto e_Exit;
  870.     }
  871.  
  872. e_Exit:
  873.     return hr;
  874. }
  875.  
  876.  
  877. //--------------------------------------------------------------------------------------
  878. // This callback function will be called immediately after the Direct3D device has been 
  879. // created, which will happen during application initialization and windowed/full screen 
  880. // toggles. This is the best location to create D3DPOOL_MANAGED resources since these 
  881. // resources need to be reloaded whenever the device is destroyed. Resources created  
  882. // here should be released in the OnDestroyDevice callback. 
  883. //--------------------------------------------------------------------------------------
  884. HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc )
  885. {
  886.     HRESULT hr;
  887.     CAllocateHierarchy Alloc;
  888.  
  889.     // Initialize the font
  890.     V_RETURN( D3DXCreateFont( pd3dDevice, 15, 0, FW_BOLD, 0, FALSE, DEFAULT_CHARSET, 
  891.                          OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
  892.                          L"Arial", &g_pFont ) );
  893.  
  894.     // Define DEBUG_VS and/or DEBUG_PS to debug vertex and/or pixel shaders with the 
  895.     // shader debugger. Debugging vertex shaders requires either REF or software vertex 
  896.     // processing, and debugging pixel shaders requires REF.  The 
  897.     // D3DXSHADER_FORCE_*_SOFTWARE_NOOPT flag improves the debug experience in the 
  898.     // shader debugger.  It enables source level debugging, prevents instruction 
  899.     // reordering, prevents dead code elimination, and forces the compiler to compile 
  900.     // against the next higher available software target, which ensures that the 
  901.     // unoptimized shaders do not exceed the shader model limitations.  Setting these 
  902.     // flags will cause slower rendering since the shaders will be unoptimized and 
  903.     // forced into software.  See the DirectX documentation for more information about 
  904.     // using the shader debugger.
  905.     DWORD dwShaderFlags = 0;
  906.     #ifdef DEBUG_VS
  907.         dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
  908.     #endif
  909.     #ifdef DEBUG_PS
  910.         dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
  911.     #endif
  912.  
  913.     // Read the D3DX effect file
  914.     WCHAR str[MAX_PATH];
  915.     V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"SkinnedMesh.fx" ) );
  916.  
  917.     // If this fails, there should be debug output as to 
  918.     // they the .fx file failed to compile
  919.     V_RETURN( D3DXCreateEffectFromFile( pd3dDevice, str, NULL, NULL, dwShaderFlags, 
  920.                                         NULL, &g_pEffect, NULL ) );
  921.  
  922.     // Load the mesh
  923.     V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, MESHFILENAME ) );
  924.     WCHAR strPath[MAX_PATH];
  925.     wcscpy( strPath, str );
  926.     WCHAR* pLastSlash = wcsrchr( strPath, L'\\' );
  927.     if( pLastSlash )
  928.         *pLastSlash = 0;
  929.     WCHAR strCWD[MAX_PATH];
  930.     GetCurrentDirectory( MAX_PATH, strCWD );
  931.     SetCurrentDirectory( strPath );
  932.     V_RETURN( D3DXLoadMeshHierarchyFromX( str, D3DXMESH_MANAGED, pd3dDevice,
  933.                                           &Alloc, NULL, &g_pFrameRoot, &g_pAnimController ) );
  934.     V_RETURN( SetupBoneMatrixPointers( g_pFrameRoot ) );
  935.     V_RETURN( D3DXFrameCalculateBoundingSphere( g_pFrameRoot, &g_vObjectCenter, &g_fObjectRadius ) );
  936.     SetCurrentDirectory( strCWD );
  937.  
  938.     // Obtain the behavior flags
  939.     D3DDEVICE_CREATION_PARAMETERS cp;
  940.     pd3dDevice->GetCreationParameters( &cp );
  941.     g_dwBehaviorFlags = cp.BehaviorFlags;
  942.  
  943.     return S_OK;
  944. }
  945.  
  946.  
  947. //--------------------------------------------------------------------------------------
  948. // This callback function will be called immediately after the Direct3D device has been 
  949. // reset, which will happen after a lost device scenario. This is the best location to 
  950. // create D3DPOOL_DEFAULT resources since these resources need to be reloaded whenever 
  951. // the device is lost. Resources created here should be released in the OnLostDevice 
  952. // callback. 
  953. //--------------------------------------------------------------------------------------
  954. HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, 
  955.                                 const D3DSURFACE_DESC* pBackBufferSurfaceDesc )
  956. {
  957.     HRESULT hr;
  958.  
  959.     if( g_pFont )
  960.         V_RETURN( g_pFont->OnResetDevice() );
  961.     if( g_pEffect )
  962.         V_RETURN( g_pEffect->OnResetDevice() );
  963.  
  964.     // Create a sprite to help batch calls when drawing many lines of text
  965.     V_RETURN( D3DXCreateSprite( pd3dDevice, &g_pTextSprite ) );
  966.  
  967.     // Setup render state
  968.     pd3dDevice->SetRenderState( D3DRS_LIGHTING,         TRUE );
  969.     pd3dDevice->SetRenderState( D3DRS_DITHERENABLE,     TRUE );
  970.     pd3dDevice->SetRenderState( D3DRS_ZENABLE,          TRUE );
  971.     pd3dDevice->SetRenderState( D3DRS_CULLMODE,         D3DCULL_CCW );
  972.     pd3dDevice->SetRenderState( D3DRS_AMBIENT,          0x33333333 );
  973.     pd3dDevice->SetRenderState( D3DRS_NORMALIZENORMALS, TRUE );
  974.     pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
  975.     pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
  976.  
  977.     // load the indexed vertex shaders
  978.     DWORD dwShaderFlags = 0;
  979.     #if defined(DEBUG_VS) || defined(DEBUG_PS)
  980.         dwShaderFlags |= D3DXSHADER_DEBUG|D3DXSHADER_SKIPVALIDATION;
  981.     #endif
  982.     for( DWORD iInfl = 0; iInfl < 4; ++iInfl )
  983.     {
  984.         LPD3DXBUFFER pCode;
  985.  
  986.         // Assemble the vertex shader file
  987.         WCHAR str[MAX_PATH];
  988.         DXUTFindDXSDKMediaFileCch( str, MAX_PATH, g_wszShaderSource[iInfl] );
  989.         if( FAILED( hr = D3DXAssembleShaderFromFile( str, NULL, NULL, dwShaderFlags, &pCode, NULL ) ) )
  990.             return hr;
  991.  
  992.         // Create the vertex shader
  993.         if( FAILED( hr = pd3dDevice->CreateVertexShader( (DWORD*)pCode->GetBufferPointer(),
  994.                                                          &g_pIndexedVertexShader[iInfl] ) ) )
  995.         {
  996.             return hr;
  997.         }
  998.  
  999.         pCode->Release();
  1000.     }
  1001.  
  1002.     // Setup the projection matrix
  1003.     float fAspect = (float)pBackBufferSurfaceDesc->Width / (float)pBackBufferSurfaceDesc->Height;
  1004.     D3DXMatrixPerspectiveFovLH( &g_matProj, D3DX_PI/4, fAspect,
  1005.                                 g_fObjectRadius/64.0f, g_fObjectRadius*200.0f );
  1006.     pd3dDevice->SetTransform( D3DTS_PROJECTION, &g_matProj );
  1007.     D3DXMatrixTranspose( &g_matProjT, &g_matProj );
  1008.  
  1009.     // Setup the arcball parameters
  1010.     g_ArcBall.SetWindow( pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height, 0.85f );
  1011.     g_ArcBall.SetTranslationRadius( g_fObjectRadius );
  1012.  
  1013.     g_HUD.SetLocation( pBackBufferSurfaceDesc->Width-170, 0 );
  1014.     g_HUD.SetSize( 170, 170 );
  1015.     g_SampleUI.SetLocation( 3, 45 );
  1016.     g_SampleUI.SetSize( 240, 70 );
  1017.  
  1018.     return S_OK;
  1019. }
  1020.  
  1021.  
  1022. //--------------------------------------------------------------------------------------
  1023. // This callback function will be called once at the beginning of every frame. This is the
  1024. // best location for your application to handle updates to the scene, but is not 
  1025. // intended to contain actual rendering calls, which should instead be placed in the 
  1026. // OnFrameRender callback.  
  1027. //--------------------------------------------------------------------------------------
  1028. void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime )
  1029. {
  1030.      // Setup world matrix
  1031.     D3DXMATRIXA16 matWorld;
  1032.     D3DXMatrixTranslation( &matWorld, -g_vObjectCenter.x,
  1033.                                       -g_vObjectCenter.y,
  1034.                                       -g_vObjectCenter.z );
  1035.     D3DXMatrixMultiply( &matWorld, &matWorld, g_ArcBall.GetRotationMatrix() );
  1036.     D3DXMatrixMultiply( &matWorld, &matWorld, g_ArcBall.GetTranslationMatrix() );
  1037.     pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
  1038.  
  1039.     D3DXVECTOR3 vEye( 0, 0, -2*g_fObjectRadius );
  1040.     D3DXVECTOR3 vAt( 0, 0, 0 );
  1041.     D3DXVECTOR3 vUp( 0, 1, 0 );
  1042.     D3DXMatrixLookAtLH( &g_matView, &vEye, &vAt, &vUp);
  1043.  
  1044.     pd3dDevice->SetTransform( D3DTS_VIEW,  &g_matView );
  1045.  
  1046.     if( g_pAnimController != NULL )
  1047.         g_pAnimController->AdvanceTime( fElapsedTime, NULL );
  1048.  
  1049.     UpdateFrameMatrices( g_pFrameRoot, &matWorld );
  1050. }
  1051.  
  1052.  
  1053. //--------------------------------------------------------------------------------------
  1054. // Called to render a mesh in the hierarchy
  1055. //--------------------------------------------------------------------------------------
  1056. void DrawMeshContainer( IDirect3DDevice9 *pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase )
  1057. {
  1058.     HRESULT hr;
  1059.     D3DXMESHCONTAINER_DERIVED *pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;
  1060.     D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;
  1061.     UINT iMaterial;
  1062.     UINT NumBlend;
  1063.     UINT iAttrib;
  1064.     DWORD AttribIdPrev;
  1065.     LPD3DXBONECOMBINATION pBoneComb;
  1066.  
  1067.     UINT iMatrixIndex;
  1068.     UINT iPaletteEntry;
  1069.     D3DXMATRIXA16 matTemp;
  1070.     D3DCAPS9 d3dCaps;
  1071.     pd3dDevice->GetDeviceCaps( &d3dCaps );
  1072.  
  1073.     // first check for skinning
  1074.     if (pMeshContainer->pSkinInfo != NULL)
  1075.     {
  1076.         if( g_SkinningMethod == D3DNONINDEXED )
  1077.         {
  1078.             AttribIdPrev = UNUSED32; 
  1079.             pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer());
  1080.  
  1081.             // Draw using default vtx processing of the device (typically HW)
  1082.             for (iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++)
  1083.             {
  1084.                 NumBlend = 0;
  1085.                 for (DWORD i = 0; i < pMeshContainer->NumInfl; ++i)
  1086.                 {
  1087.                     if (pBoneComb[iAttrib].BoneId[i] != UINT_MAX)
  1088.                     {
  1089.                         NumBlend = i;
  1090.                     }
  1091.                 }
  1092.  
  1093.                 if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
  1094.                 {
  1095.                     // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
  1096.                     for (DWORD i = 0; i < pMeshContainer->NumInfl; ++i)
  1097.                     {
  1098.                         iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
  1099.                         if (iMatrixIndex != UINT_MAX)
  1100.                         {
  1101.                             D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
  1102.                             V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
  1103.                         }
  1104.                     }
  1105.  
  1106.                     V( pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, NumBlend) );
  1107.  
  1108.                     // lookup the material used for this subset of faces
  1109.                     if ((AttribIdPrev != pBoneComb[iAttrib].AttribId) || (AttribIdPrev == UNUSED32))
  1110.                     {
  1111.                         V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
  1112.                         V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
  1113.                         AttribIdPrev = pBoneComb[iAttrib].AttribId;
  1114.                     }
  1115.  
  1116.                     // draw the subset now that the correct material and matrices are loaded
  1117.                     V( pMeshContainer->MeshData.pMesh->DrawSubset(iAttrib) );
  1118.                 }
  1119.             }
  1120.  
  1121.             // If necessary, draw parts that HW could not handle using SW
  1122.             if (pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups)
  1123.             {
  1124.                 AttribIdPrev = UNUSED32; 
  1125.                 V( pd3dDevice->SetSoftwareVertexProcessing(TRUE) );
  1126.                 for (iAttrib = pMeshContainer->iAttributeSW; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++)
  1127.                 {
  1128.                     NumBlend = 0;
  1129.                     for (DWORD i = 0; i < pMeshContainer->NumInfl; ++i)
  1130.                     {
  1131.                         if (pBoneComb[iAttrib].BoneId[i] != UINT_MAX)
  1132.                         {
  1133.                             NumBlend = i;
  1134.                         }
  1135.                     }
  1136.  
  1137.                     if (d3dCaps.MaxVertexBlendMatrices < NumBlend + 1)
  1138.                     {
  1139.                         // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
  1140.                         for (DWORD i = 0; i < pMeshContainer->NumInfl; ++i)
  1141.                         {
  1142.                             iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
  1143.                             if (iMatrixIndex != UINT_MAX)
  1144.                             {
  1145.                                 D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
  1146.                                 V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
  1147.                             }
  1148.                         }
  1149.  
  1150.                         V( pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, NumBlend) );
  1151.  
  1152.                         // lookup the material used for this subset of faces
  1153.                         if ((AttribIdPrev != pBoneComb[iAttrib].AttribId) || (AttribIdPrev == UNUSED32))
  1154.                         {
  1155.                             V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
  1156.                             V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
  1157.                             AttribIdPrev = pBoneComb[iAttrib].AttribId;
  1158.                         }
  1159.  
  1160.                         // draw the subset now that the correct material and matrices are loaded
  1161.                         V( pMeshContainer->MeshData.pMesh->DrawSubset(iAttrib) );
  1162.                     }
  1163.                 }
  1164.                 V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) );
  1165.             }
  1166.  
  1167.             V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0) );
  1168.         }
  1169.         else if (g_SkinningMethod == D3DINDEXED)
  1170.         {
  1171.             // if hw doesn't support indexed vertex processing, switch to software vertex processing
  1172.             if (pMeshContainer->UseSoftwareVP)
  1173.             {
  1174.                 // If hw or pure hw vertex processing is forced, we can't render the
  1175.                 // mesh, so just exit out.  Typical applications should create
  1176.                 // a device with appropriate vertex processing capability for this
  1177.                 // skinning method.
  1178.                 if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING )
  1179.                     return;
  1180.  
  1181.                 V( pd3dDevice->SetSoftwareVertexProcessing(TRUE) );
  1182.             }
  1183.  
  1184.             // set the number of vertex blend indices to be blended
  1185.             if (pMeshContainer->NumInfl == 1)
  1186.             {
  1187.                 V( pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_0WEIGHTS) );
  1188.             }
  1189.             else
  1190.             {
  1191.                 V( pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, pMeshContainer->NumInfl - 1) );
  1192.             }
  1193.  
  1194.             if (pMeshContainer->NumInfl)
  1195.                 V( pd3dDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE) );
  1196.  
  1197.             // for each attribute group in the mesh, calculate the set of matrices in the palette and then draw the mesh subset
  1198.             pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer());
  1199.             for (iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++)
  1200.             {
  1201.                 // first calculate all the world matrices
  1202.                 for (iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries; ++iPaletteEntry)
  1203.                 {
  1204.                     iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry];
  1205.                     if (iMatrixIndex != UINT_MAX)
  1206.                     {
  1207.                         D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
  1208.                         V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( iPaletteEntry ), &matTemp ) );
  1209.                     }
  1210.                 }
  1211.                 
  1212.                 // setup the material of the mesh subset - REMEMBER to use the original pre-skinning attribute id to get the correct material id
  1213.                 V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
  1214.                 V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
  1215.  
  1216.                 // finally draw the subset with the current world matrix palette and material state
  1217.                 V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
  1218.             }
  1219.  
  1220.             // reset blending state
  1221.             V( pd3dDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE) );
  1222.             V( pd3dDevice->SetRenderState(D3DRS_VERTEXBLEND, 0) );
  1223.  
  1224.             // remember to reset back to hw vertex processing if software was required
  1225.             if (pMeshContainer->UseSoftwareVP)
  1226.             {
  1227.                 V( pd3dDevice->SetSoftwareVertexProcessing(FALSE) );
  1228.             }
  1229.         }
  1230.         else if (g_SkinningMethod == D3DINDEXEDVS) 
  1231.         {
  1232.             // Use COLOR instead of UBYTE4 since Geforce3 does not support it
  1233.             // vConst.w should be 3, but due to COLOR/UBYTE4 issue, mul by 255 and add epsilon
  1234.             D3DXVECTOR4 vConst( 1.0f, 0.0f, 0.0f, 765.01f );
  1235.  
  1236.             if (pMeshContainer->UseSoftwareVP)
  1237.             {
  1238.                 // If hw or pure hw vertex processing is forced, we can't render the
  1239.                 // mesh, so just exit out.  Typical applications should create
  1240.                 // a device with appropriate vertex processing capability for this
  1241.                 // skinning method.
  1242.                 if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING )
  1243.                     return;
  1244.  
  1245.                 V( pd3dDevice->SetSoftwareVertexProcessing(TRUE) );
  1246.             }
  1247.  
  1248.             V( pd3dDevice->SetVertexShader( g_pIndexedVertexShader[pMeshContainer->NumInfl - 1] ) );
  1249.  
  1250.             pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer());
  1251.             for (iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++)
  1252.             {
  1253.                 // first calculate all the world matrices
  1254.                 for (iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries; ++iPaletteEntry)
  1255.                 {
  1256.                     iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry];
  1257.                     if (iMatrixIndex != UINT_MAX)
  1258.                     {
  1259.                         D3DXMatrixMultiply(&matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex]);
  1260.                         D3DXMatrixMultiplyTranspose(&matTemp, &matTemp, &g_matView);
  1261.                         V( pd3dDevice->SetVertexShaderConstantF(iPaletteEntry*3 + 9, (float*)&matTemp, 3) );
  1262.                     }
  1263.                 }
  1264.  
  1265.                 // Sum of all ambient and emissive contribution
  1266.                 D3DXCOLOR color1(pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Ambient);
  1267.                 D3DXCOLOR color2(.25, .25, .25, 1.0);
  1268.                 D3DXCOLOR ambEmm;
  1269.                 D3DXColorModulate(&ambEmm, &color1, &color2);
  1270.                 ambEmm += D3DXCOLOR(pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Emissive);
  1271.  
  1272.                 // set material color properties 
  1273.                 V( pd3dDevice->SetVertexShaderConstantF(8, (float*)&(pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Diffuse), 1) );
  1274.                 V( pd3dDevice->SetVertexShaderConstantF(7, (float*)&ambEmm, 1) );
  1275.                 vConst.y = pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Power;
  1276.                 V( pd3dDevice->SetVertexShaderConstantF(0, (float*)&vConst, 1) );
  1277.  
  1278.                 V( pd3dDevice->SetTexture(0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId]) );
  1279.  
  1280.                 // finally draw the subset with the current world matrix palette and material state
  1281.                 V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
  1282.             }
  1283.  
  1284.             // remember to reset back to hw vertex processing if software was required
  1285.             if (pMeshContainer->UseSoftwareVP)
  1286.             {
  1287.                 V( pd3dDevice->SetSoftwareVertexProcessing(FALSE) );
  1288.             }
  1289.             V( pd3dDevice->SetVertexShader( NULL ) );
  1290.         }
  1291.         else if (g_SkinningMethod == D3DINDEXEDHLSLVS) 
  1292.         {
  1293.             if (pMeshContainer->UseSoftwareVP)
  1294.             {
  1295.                 // If hw or pure hw vertex processing is forced, we can't render the
  1296.                 // mesh, so just exit out.  Typical applications should create
  1297.                 // a device with appropriate vertex processing capability for this
  1298.                 // skinning method.
  1299.                 if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING )
  1300.                     return;
  1301.  
  1302.                 V( pd3dDevice->SetSoftwareVertexProcessing(TRUE) );
  1303.             }
  1304.  
  1305.             pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>(pMeshContainer->pBoneCombinationBuf->GetBufferPointer());
  1306.             for (iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++)
  1307.             { 
  1308.                 // first calculate all the world matrices
  1309.                 for (iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries; ++iPaletteEntry)
  1310.                 {
  1311.                     iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry];
  1312.                     if (iMatrixIndex != UINT_MAX)
  1313.                     {
  1314.                         D3DXMatrixMultiply(&matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex], pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex]);
  1315.                         D3DXMatrixMultiply(&g_pBoneMatrices[iPaletteEntry], &matTemp, &g_matView);
  1316.                     }
  1317.                 }
  1318.                 V( g_pEffect->SetMatrixArray( "mWorldMatrixArray", g_pBoneMatrices, pMeshContainer->NumPaletteEntries) );
  1319.  
  1320.                 // Sum of all ambient and emissive contribution
  1321.                 D3DXCOLOR color1(pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Ambient);
  1322.                 D3DXCOLOR color2(.25, .25, .25, 1.0);
  1323.                 D3DXCOLOR ambEmm;
  1324.                 D3DXColorModulate(&ambEmm, &color1, &color2);
  1325.                 ambEmm += D3DXCOLOR(pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Emissive);
  1326.  
  1327.                 // set material color properties 
  1328.                 V( g_pEffect->SetVector("MaterialDiffuse", (D3DXVECTOR4*)&(pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Diffuse)) );
  1329.                 V( g_pEffect->SetVector("MaterialAmbient", (D3DXVECTOR4*)&ambEmm) );
  1330.  
  1331.                 // setup the material of the mesh subset - REMEMBER to use the original pre-skinning attribute id to get the correct material id
  1332.                 V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
  1333.  
  1334.                 // Set CurNumBones to select the correct vertex shader for the number of bones
  1335.                 V( g_pEffect->SetInt( "CurNumBones", pMeshContainer->NumInfl -1) );
  1336.  
  1337.                 // Start the effect now all parameters have been updated
  1338.                 UINT numPasses;
  1339.                 V( g_pEffect->Begin( &numPasses, D3DXFX_DONOTSAVESTATE ) );
  1340.                 for( UINT iPass = 0; iPass < numPasses; iPass++ )
  1341.                 {
  1342.                     V( g_pEffect->BeginPass( iPass ) );
  1343.  
  1344.                     // draw the subset with the current world matrix palette and material state
  1345.                     V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
  1346.  
  1347.                     V( g_pEffect->EndPass() );
  1348.                 }
  1349.  
  1350.                 V( g_pEffect->End() );
  1351.  
  1352.                 V( pd3dDevice->SetVertexShader(NULL) );
  1353.             }
  1354.  
  1355.             // remember to reset back to hw vertex processing if software was required
  1356.             if (pMeshContainer->UseSoftwareVP)
  1357.             {
  1358.                 V( pd3dDevice->SetSoftwareVertexProcessing(FALSE) );
  1359.             }
  1360.         }
  1361.         else if (g_SkinningMethod == SOFTWARE)
  1362.         {
  1363.             D3DXMATRIX  Identity;
  1364.             DWORD       cBones  = pMeshContainer->pSkinInfo->GetNumBones();
  1365.             DWORD       iBone;
  1366.             PBYTE       pbVerticesSrc;
  1367.             PBYTE       pbVerticesDest;
  1368.  
  1369.             // set up bone transforms
  1370.             for (iBone = 0; iBone < cBones; ++iBone)
  1371.             {
  1372.                 D3DXMatrixMultiply
  1373.                 (
  1374.                     &g_pBoneMatrices[iBone],                 // output
  1375.                     &pMeshContainer->pBoneOffsetMatrices[iBone], 
  1376.                     pMeshContainer->ppBoneMatrixPtrs[iBone]
  1377.                 );
  1378.             }
  1379.  
  1380.             // set world transform
  1381.             D3DXMatrixIdentity(&Identity);
  1382.             V( pd3dDevice->SetTransform(D3DTS_WORLD, &Identity) );
  1383.  
  1384.             V( pMeshContainer->pOrigMesh->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&pbVerticesSrc) );
  1385.             V( pMeshContainer->MeshData.pMesh->LockVertexBuffer(0, (LPVOID*)&pbVerticesDest) );
  1386.  
  1387.             // generate skinned mesh
  1388.             pMeshContainer->pSkinInfo->UpdateSkinnedMesh(g_pBoneMatrices, NULL, pbVerticesSrc, pbVerticesDest);
  1389.  
  1390.             V( pMeshContainer->pOrigMesh->UnlockVertexBuffer() );
  1391.             V( pMeshContainer->MeshData.pMesh->UnlockVertexBuffer() );
  1392.  
  1393.             for (iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++)
  1394.             {
  1395.                 V( pd3dDevice->SetMaterial(&(pMeshContainer->pMaterials[pMeshContainer->pAttributeTable[iAttrib].AttribId].MatD3D)) );
  1396.                 V( pd3dDevice->SetTexture(0, pMeshContainer->ppTextures[pMeshContainer->pAttributeTable[iAttrib].AttribId]) );
  1397.                 V( pMeshContainer->MeshData.pMesh->DrawSubset(pMeshContainer->pAttributeTable[iAttrib].AttribId) );
  1398.             }
  1399.         }
  1400.         else // bug out as unsupported mode
  1401.         {
  1402.             return;
  1403.         }
  1404.     }
  1405.     else  // standard mesh, just draw it after setting material properties
  1406.     {
  1407.         V( pd3dDevice->SetTransform(D3DTS_WORLD, &pFrame->CombinedTransformationMatrix) );
  1408.  
  1409.         for (iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++)
  1410.         {
  1411.             V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D ) );
  1412.             V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[iMaterial] ) );
  1413.             V( pMeshContainer->MeshData.pMesh->DrawSubset(iMaterial) );
  1414.         }
  1415.     }
  1416. }
  1417.  
  1418.  
  1419.  
  1420.  
  1421. //--------------------------------------------------------------------------------------
  1422. // Called to render a frame in the hierarchy
  1423. //--------------------------------------------------------------------------------------
  1424. void DrawFrame( IDirect3DDevice9 *pd3dDevice, LPD3DXFRAME pFrame )
  1425. {
  1426.     LPD3DXMESHCONTAINER pMeshContainer;
  1427.  
  1428.     pMeshContainer = pFrame->pMeshContainer;
  1429.     while (pMeshContainer != NULL)
  1430.     {
  1431.         DrawMeshContainer( pd3dDevice, pMeshContainer, pFrame );
  1432.  
  1433.         pMeshContainer = pMeshContainer->pNextMeshContainer;
  1434.     }
  1435.  
  1436.     if (pFrame->pFrameSibling != NULL)
  1437.     {
  1438.         DrawFrame( pd3dDevice, pFrame->pFrameSibling);
  1439.     }
  1440.  
  1441.     if (pFrame->pFrameFirstChild != NULL)
  1442.     {
  1443.         DrawFrame( pd3dDevice, pFrame->pFrameFirstChild );
  1444.     }
  1445. }
  1446.  
  1447.  
  1448. //--------------------------------------------------------------------------------------
  1449. // This callback function will be called at the end of every frame to perform all the 
  1450. // rendering calls for the scene, and it will also be called if the window needs to be 
  1451. // repainted. After this function has returned, the sample framework will call 
  1452. // IDirect3DDevice9::Present to display the contents of the next buffer in the swap chain
  1453. //--------------------------------------------------------------------------------------
  1454. void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime )
  1455. {
  1456.     HRESULT hr;
  1457.  
  1458.     // Clear the backbuffer
  1459.     pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
  1460.                          D3DCOLOR_ARGB(0, 66, 75, 121), 1.0f, 0L );
  1461.  
  1462.     // Setup the light
  1463.     D3DLIGHT9 light;
  1464.     D3DXVECTOR3 vecLightDirUnnormalized(0.0f, -1.0f, 1.0f);
  1465.     ZeroMemory( &light, sizeof(D3DLIGHT9) );
  1466.     light.Type        = D3DLIGHT_DIRECTIONAL;
  1467.     light.Diffuse.r   = 1.0f;
  1468.     light.Diffuse.g   = 1.0f;
  1469.     light.Diffuse.b   = 1.0f;
  1470.     D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecLightDirUnnormalized );
  1471.     light.Position.x   = 0.0f;
  1472.     light.Position.y   = -1.0f;
  1473.     light.Position.z   = 1.0f;
  1474.     light.Range        = 1000.0f;
  1475.     V( pd3dDevice->SetLight(0, &light ) );
  1476.     V( pd3dDevice->LightEnable(0, TRUE ) );
  1477.  
  1478.     // Set the projection matrix for the vertex shader based skinning method
  1479.     if( g_SkinningMethod == D3DINDEXEDVS )
  1480.     {
  1481.         V( pd3dDevice->SetVertexShaderConstantF( 2, (float*)&g_matProjT, 4 ) );
  1482.     }
  1483.     else
  1484.     if( g_SkinningMethod == D3DINDEXEDHLSLVS )
  1485.     {
  1486.         V( g_pEffect->SetMatrix( "mViewProj", &g_matProj ) );
  1487.     }
  1488.  
  1489.     // Set Light for vertex shader
  1490.     D3DXVECTOR4 vLightDir( 0.0f, 1.0f, -1.0f, 0.0f );
  1491.     D3DXVec4Normalize( &vLightDir, &vLightDir );
  1492.     V( pd3dDevice->SetVertexShaderConstantF(1, (float*)&vLightDir, 1) );
  1493.     V( g_pEffect->SetVector( "lhtDir", &vLightDir) );
  1494.  
  1495.     // Begin the scene
  1496.     if( SUCCEEDED( pd3dDevice->BeginScene() ) )
  1497.     {
  1498.         DrawFrame( pd3dDevice, g_pFrameRoot );
  1499.  
  1500.         RenderText();
  1501.         V( g_HUD.OnRender( fElapsedTime ) );
  1502.         V( g_SampleUI.OnRender( fElapsedTime ) );
  1503.  
  1504.         // End the scene.
  1505.         pd3dDevice->EndScene();
  1506.     }
  1507. }
  1508.  
  1509.  
  1510. //--------------------------------------------------------------------------------------
  1511. // Render the help and statistics text. This function uses the ID3DXFont interface for 
  1512. // efficient text rendering.
  1513. //--------------------------------------------------------------------------------------
  1514. void RenderText()
  1515. {
  1516.     // The helper object simply helps keep track of text position, and color
  1517.     // and then it calls pFont->DrawText( m_pSprite, strMsg, -1, &rc, DT_NOCLIP, m_clr );
  1518.     // If NULL is passed in as the sprite object, then it will work however the 
  1519.     // pFont->DrawText() will not be batched together.  Batching calls will improves performance.
  1520.     const D3DSURFACE_DESC* pd3dsdBackBuffer = DXUTGetBackBufferSurfaceDesc();
  1521.     CDXUTTextHelper txtHelper( g_pFont, g_pTextSprite, 15 );
  1522.  
  1523.     // Output statistics
  1524.     txtHelper.Begin();
  1525.     txtHelper.SetInsertionPos( 5, 5 );
  1526.     txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
  1527.     txtHelper.DrawTextLine( DXUTGetFrameStats() );
  1528.     txtHelper.DrawTextLine( DXUTGetDeviceStats() );
  1529.  
  1530.     txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
  1531.     // Output statistics
  1532.     switch( g_SkinningMethod )
  1533.     {
  1534.         case D3DNONINDEXED:
  1535.             txtHelper.DrawTextLine( L"Using fixed-function non-indexed skinning\n" );
  1536.             break;
  1537.         case D3DINDEXED:
  1538.             txtHelper.DrawTextLine( L"Using fixed-function indexed skinning\n" );
  1539.             break;
  1540.         case SOFTWARE:
  1541.             txtHelper.DrawTextLine( L"Using software skinning\n" );
  1542.             break;
  1543.         case D3DINDEXEDVS:
  1544.             txtHelper.DrawTextLine( L"Using assembly vertex shader indexed skinning\n" );
  1545.             break;
  1546.         case D3DINDEXEDHLSLVS:
  1547.             txtHelper.DrawTextLine( L"Using HLSL vertex shader indexed skinning\n" );
  1548.             break;
  1549.         default:
  1550.             txtHelper.DrawTextLine( L"No skinning\n" );
  1551.     }
  1552.  
  1553.     // Draw help
  1554.     if( g_bShowHelp )
  1555.     {
  1556.         txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*6 );
  1557.         txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );
  1558.         txtHelper.DrawTextLine( L"Controls (F1 to hide):" );
  1559.  
  1560.         txtHelper.SetInsertionPos( 40, pd3dsdBackBuffer->Height-15*5 );
  1561.         txtHelper.DrawTextLine( L"Rotate model: Left click drag\n"
  1562.                                 L"Zoom: Middle click drag\n"
  1563.                                 L"Pane: Right click drag\n"
  1564.                                 L"Quit: ESC" );
  1565.     }
  1566.     else
  1567.     {
  1568.         txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*2 );
  1569.         txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
  1570.         txtHelper.DrawTextLine( L"Press F1 for help" );
  1571.     }
  1572.  
  1573.     // If software vp is required and we are using a hwvp device,
  1574.     // the mesh is not being displayed and we output an error message here.
  1575.     if( g_bUseSoftwareVP && ( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING ) )
  1576.     {
  1577.         txtHelper.SetInsertionPos( 5, 85 );
  1578.         txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f ) );
  1579.         txtHelper.DrawTextLine( L"The HWVP device does not support this skinning method.\n"
  1580.                                 L"Select another skinning method or switch to mixed or software VP." );
  1581.     }
  1582.  
  1583.     txtHelper.End();
  1584. }
  1585.  
  1586.  
  1587. //--------------------------------------------------------------------------------------
  1588. // Before handling window messages, the sample framework passes incoming windows 
  1589. // messages to the application through this callback function. If the application sets 
  1590. // *pbNoFurtherProcessing to TRUE, then the sample framework will not process this message.
  1591. //--------------------------------------------------------------------------------------
  1592. LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing )
  1593. {
  1594.     // Give the dialogs a chance to handle the message first
  1595.     *pbNoFurtherProcessing = g_HUD.MsgProc( hWnd, uMsg, wParam, lParam );
  1596.     if( *pbNoFurtherProcessing )
  1597.         return 0;
  1598.     *pbNoFurtherProcessing = g_SampleUI.MsgProc( hWnd, uMsg, wParam, lParam );
  1599.     if( *pbNoFurtherProcessing )
  1600.         return 0;
  1601.  
  1602.     // Pass all remaining windows messages to arcball so it can respond to user input
  1603.     g_ArcBall.HandleMessages( hWnd, uMsg, wParam, lParam );
  1604.  
  1605.     return 0;
  1606. }
  1607.  
  1608.  
  1609. //--------------------------------------------------------------------------------------
  1610. // As a convenience, the sample framework inspects the incoming windows messages for
  1611. // keystroke messages and decodes the message parameters to pass relevant keyboard
  1612. // messages to the application.  The framework does not remove the underlying keystroke 
  1613. // messages, which are still passed to the application's MsgProc callback.
  1614. //--------------------------------------------------------------------------------------
  1615. void CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown )
  1616. {
  1617.     if( bKeyDown )
  1618.     {
  1619.         switch( nChar )
  1620.         {
  1621.             case VK_F1: g_bShowHelp = !g_bShowHelp; break;
  1622.         }
  1623.     }
  1624. }
  1625.  
  1626.  
  1627. //--------------------------------------------------------------------------------------
  1628. // Handles the GUI events
  1629. //--------------------------------------------------------------------------------------
  1630. void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl )
  1631. {
  1632.     switch( nControlID )
  1633.     {
  1634.         case IDC_TOGGLEFULLSCREEN: DXUTToggleFullScreen(); break;
  1635.         case IDC_TOGGLEREF:        DXUTToggleREF(); break;
  1636.         case IDC_CHANGEDEVICE:     DXUTSetShowSettingsDialog( !DXUTGetShowSettingsDialog() ); break;
  1637.         case IDC_METHOD:
  1638.         {
  1639.             METHOD NewSkinningMethod = (METHOD)(size_t)((CDXUTComboBox*)pControl)->GetSelectedData();
  1640.  
  1641.             // If the selected skinning method is different than the current one
  1642.             if( g_SkinningMethod != NewSkinningMethod )
  1643.             {
  1644.                 g_SkinningMethod = NewSkinningMethod;
  1645.  
  1646.                 // update the meshes to the new skinning method
  1647.                 UpdateSkinningMethod( g_pFrameRoot );
  1648.             }
  1649.             break;
  1650.         }
  1651.     }
  1652. }
  1653.  
  1654.  
  1655. //--------------------------------------------------------------------------------------
  1656. // This callback function will be called immediately after the Direct3D device has 
  1657. // entered a lost state and before IDirect3DDevice9::Reset is called. Resources created
  1658. // in the OnResetDevice callback should be released here, which generally includes all 
  1659. // D3DPOOL_DEFAULT resources. See the "Lost Devices" section of the documentation for 
  1660. // information about lost devices.
  1661. //--------------------------------------------------------------------------------------
  1662. void CALLBACK OnLostDevice()
  1663. {
  1664.     if( g_pFont )
  1665.         g_pFont->OnLostDevice();
  1666.     if( g_pEffect )
  1667.         g_pEffect->OnLostDevice();
  1668.     SAFE_RELEASE( g_pTextSprite );
  1669.  
  1670.     // Release the vertex shaders
  1671.     for( DWORD iInfl = 0; iInfl < 4; ++iInfl )
  1672.         SAFE_RELEASE( g_pIndexedVertexShader[iInfl] );
  1673. }
  1674.  
  1675.  
  1676. //--------------------------------------------------------------------------------------
  1677. // This callback function will be called immediately after the Direct3D device has 
  1678. // been destroyed, which generally happens as a result of application termination or 
  1679. // windowed/full screen toggles. Resources created in the OnCreateDevice callback 
  1680. // should be released here, which generally includes all D3DPOOL_MANAGED resources. 
  1681. //--------------------------------------------------------------------------------------
  1682. void CALLBACK OnDestroyDevice()
  1683. {
  1684.     SAFE_RELEASE( g_pEffect );
  1685.     SAFE_RELEASE( g_pFont );
  1686.  
  1687.     CAllocateHierarchy Alloc;
  1688.     D3DXFrameDestroy( g_pFrameRoot, &Alloc );
  1689.     SAFE_RELEASE( g_pAnimController );
  1690. }
  1691.  
  1692.  
  1693. //--------------------------------------------------------------------------------------
  1694. // Called to setup the pointers for a given bone to its transformation matrix
  1695. //--------------------------------------------------------------------------------------
  1696. HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase )
  1697. {
  1698.     UINT iBone, cBones;
  1699.     D3DXFRAME_DERIVED *pFrame;
  1700.  
  1701.     D3DXMESHCONTAINER_DERIVED *pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;
  1702.  
  1703.     // if there is a skinmesh, then setup the bone matrices
  1704.     if (pMeshContainer->pSkinInfo != NULL)
  1705.     {
  1706.         cBones = pMeshContainer->pSkinInfo->GetNumBones();
  1707.  
  1708.         pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];
  1709.         if (pMeshContainer->ppBoneMatrixPtrs == NULL)
  1710.             return E_OUTOFMEMORY;
  1711.  
  1712.         for (iBone = 0; iBone < cBones; iBone++)
  1713.         {
  1714.             pFrame = (D3DXFRAME_DERIVED*)D3DXFrameFind( g_pFrameRoot, pMeshContainer->pSkinInfo->GetBoneName(iBone) );
  1715.             if (pFrame == NULL)
  1716.                 return E_FAIL;
  1717.  
  1718.             pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix;
  1719.         }
  1720.     }
  1721.  
  1722.     return S_OK;
  1723. }
  1724.  
  1725.  
  1726. //--------------------------------------------------------------------------------------
  1727. // Called to setup the pointers for a given bone to its transformation matrix
  1728. //--------------------------------------------------------------------------------------
  1729. HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrame )
  1730. {
  1731.     HRESULT hr;
  1732.  
  1733.     if (pFrame->pMeshContainer != NULL)
  1734.     {
  1735.         hr = SetupBoneMatrixPointersOnMesh(pFrame->pMeshContainer);
  1736.         if (FAILED(hr))
  1737.             return hr;
  1738.     }
  1739.  
  1740.     if (pFrame->pFrameSibling != NULL)
  1741.     {
  1742.         hr = SetupBoneMatrixPointers(pFrame->pFrameSibling);
  1743.         if (FAILED(hr))
  1744.             return hr;
  1745.     }
  1746.  
  1747.     if (pFrame->pFrameFirstChild != NULL)
  1748.     {
  1749.         hr = SetupBoneMatrixPointers(pFrame->pFrameFirstChild);
  1750.         if (FAILED(hr))
  1751.             return hr;
  1752.     }
  1753.  
  1754.     return S_OK;
  1755. }
  1756.  
  1757.  
  1758.  
  1759.  
  1760. //--------------------------------------------------------------------------------------
  1761. // update the frame matrices
  1762. //--------------------------------------------------------------------------------------
  1763. void UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix )
  1764. {
  1765.     D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;
  1766.  
  1767.     if (pParentMatrix != NULL)
  1768.         D3DXMatrixMultiply(&pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix);
  1769.     else
  1770.         pFrame->CombinedTransformationMatrix = pFrame->TransformationMatrix;
  1771.  
  1772.     if (pFrame->pFrameSibling != NULL)
  1773.     {
  1774.         UpdateFrameMatrices(pFrame->pFrameSibling, pParentMatrix);
  1775.     }
  1776.  
  1777.     if (pFrame->pFrameFirstChild != NULL)
  1778.     {
  1779.         UpdateFrameMatrices(pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix);
  1780.     }
  1781. }
  1782.  
  1783.  
  1784. //--------------------------------------------------------------------------------------
  1785. // update the skinning method
  1786. //--------------------------------------------------------------------------------------
  1787. void UpdateSkinningMethod( LPD3DXFRAME pFrameBase )
  1788. {
  1789.     D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED*)pFrameBase;
  1790.     D3DXMESHCONTAINER_DERIVED *pMeshContainer;
  1791.  
  1792.     pMeshContainer = (D3DXMESHCONTAINER_DERIVED *)pFrame->pMeshContainer;
  1793.  
  1794.     while( pMeshContainer != NULL )
  1795.     {
  1796.         GenerateSkinnedMesh( DXUTGetD3DDevice(), pMeshContainer );
  1797.  
  1798.         pMeshContainer = (D3DXMESHCONTAINER_DERIVED *)pMeshContainer->pNextMeshContainer;
  1799.     }
  1800.  
  1801.     if (pFrame->pFrameSibling != NULL)
  1802.     {
  1803.         UpdateSkinningMethod(pFrame->pFrameSibling);
  1804.     }
  1805.  
  1806.     if (pFrame->pFrameFirstChild != NULL)
  1807.     {
  1808.         UpdateSkinningMethod(pFrame->pFrameFirstChild);
  1809.     }
  1810. }
  1811.